<!--
 * @Date: 2024-08-23 15:04:10
 * @Author: LiSong
 * @LastEditors: LiSong
 * @LastEditTime: 2025-04-22 10:51:19
 * @Description: 
-->
<template>
  <div class="ace-editor" :style="editorStyle">
    <div class="ace-editor--inner" ref="editorRef"></div>
  </div>
</template>

<script setup>
import ace from 'ace-builds';
import '../../cfg/ace-editor-resolver';
import { aceAutoTips } from '../../cfg/ace-auto-tips'

import { ref, reactive, onMounted, computed, watch } from 'vue'

// 语法校验提示
import workerCssUrl from 'ace-builds/src-min-noconflict/worker-css.js?url';
import workerBaseUrl from 'ace-builds/src-min-noconflict/worker-base?url';
import workerJsonUrl from 'ace-builds/src-min-noconflict/worker-json?url';
import workerJavascriptUrl from 'ace-builds/src-min-noconflict/worker-javascript?url';

// 定义对外触发事件
const emits = defineEmits(['update:modelValue', 'change'])

// 定义对内传递参数
const props = defineProps({
  // 编辑器模式：javascript|sqlserver
  mode: { type: String, default: 'javascript' },
  // 编辑器主题
  theme: { type: String, default: 'sqlserver' },
  // 边框线
  border: { type: Boolean, default: false },
  // 缩进长度
  tabSize: { type: Number, default: 2 },
  // 是否只读
  readonly: { type: Boolean, default: false },
  // 最小行数
  minLines: { type: Number, default: 25 },
  // 字体大小
  fontSize: { type: Number, default: 12 },
  // 是否开启语法校验，默认开启
  userWorker: { type: Boolean, default: true },
  // 用于v-model
  modelValue: { type: String, default: '' },
})

// 编辑器对象
const editor = ref(null)
// 编辑器Ref对象
const editorRef = ref(null)
// 编辑器配置
const editorOptions = reactive({
  // 语法模式
  mode: `ace/mode/${props.mode}`,
  // 主题
  theme: `ace/theme/${props.theme}`,
  // 编辑器内容
  value: props.modelValue,
  // 制表符设置2个空格
  tabSize: props.tabSize,
  // 最大行数
  // maxLines: props.maxLines,
  // 最小行数
  minLines: props.minLines,
  // 字体大小
  fontSize: props.fontSize,
  // 是否只读
  readOnly: props.readonly,
  // 使用辅助对象
  useWorker: props.userWorker,
  // 是否显示行号
  showLineNumbers: true,
  // 启用代码片段提示
  enableSnippets: true,
  // 设置编辑器中间的竖线显示
  showPrintMargin: false,
  // 高亮当前行
  highlightActiveLine: true,
  // 高亮选中文本
  highlightSelectedWord: true,
  // 启用实时自动完成
  enableLiveAutocompletion: true,
  // 启用基本自动完成
  enableBasicAutocompletion: true,
  
})
watch(
  () => props.mode,
  (nv, ov) => {
    if (!editor.value) return
    editorOptions.mode = `ace/mode/${nv}`
    initEditor()
    setValue(props.modelValue)
  },
  { deep: true, immediate: false }
)

// 编辑器样式
const editorStyle = computed(() => {
  let result = {}
  if (props.border) {
    result['border'] = '1px solid #ececec';
    
  }
  return result
})
/**
 * 初始化编辑器
 */
const initEditor = () => {
  destoryEditor();
  loadEditorWorker();
  editor.value = ace.edit(editorRef.value, editorOptions);
  loadEditorEvents();
  loadCustomPrompt();
}
/**
 * 销毁编辑器
 */
const destoryEditor = () => {
  if (editor.value) editor.value.destroy();
}
/**
 * 加载编辑器的校验规则
 */
const loadEditorWorker = () => {
  ace.config.setModuleUrl('ace/mode/css_worker', workerCssUrl);
  ace.config.setModuleUrl('ace/mode/base_worker', workerBaseUrl);
  ace.config.setModuleUrl('ace/mode/json_worker', workerJsonUrl);
  ace.config.setModuleUrl('ace/mode/javascript_worker', workerJavascriptUrl);
}
/**
 * 加载编辑器的事件配置
 */
const loadEditorEvents = () => {
  editor.value.on('change', () => {
    emits('change',  editor.value.getSession().getValue());
    emits('update:modelValue',  editor.value.getSession().getValue());
  });
}
/**
 * 加载编辑器的自定义提示内容
 */
const loadCustomPrompt = () => {
  editor.value.completers.push({
    getCompletions: function (editor, session, pos, prefix, callback) {
      return callback(null, aceAutoTips);
    },
  });
}

/**
 * 设置编辑器内容
 *
 * @param {String} content 编辑器内容
 */
const setValue = (content) => {
  if (typeof content === 'string' || content instanceof String) {
    if (editor) editor.value.getSession().setValue(content);
  } else {
    console.warn('content格式只能是字符串类型！')
  }
}

/**
 * 获取编辑器验证信息
 */
const getAnnotations = () => {
  return editor.value.getSession().getAnnotations();
}

// 组件完成初始渲染后
onMounted(() => {
  initEditor()
})

defineExpose({
  setValue,
  getAnnotations
})


</script>

<style lang="scss" scoped>
.ace-editor {
  height: 100%;
  border: 1px solid #ececec;
  &--inner {
    height: 100%;
    min-height: 300px;
  }
}
</style>